---
title: TypeScript functions
description: Learn how to create and use EAS Build functions in your custom build configurations.
---

import { GithubIcon } from '@expo/styleguide-icons/custom/GithubIcon';

import { BoxLink } from '~/ui/components/BoxLink';
import { ContentSpotlight } from '~/ui/components/ContentSpotlight';
import { Terminal } from '~/ui/components/Snippet';
import { Step } from '~/ui/components/Step';

EAS Build functions are a great way to extend the functionality of custom builds. You can use them to create reusable steps, and to write your logic in JavaScript, TypeScript, or Bash (more information in [`command` in the config schema](./schema.mdx#functionsfunction_namecommand)). This guide provides a walkthrough of creating a function in TypeScript.

<Step label="1">

## Initialize an EAS Build function module

The easiest way to create an EAS Build function is to use the `create-eas-build-function` CLI tool. By running the following command from the same directory as your **eas.json** file, you can create a new custom TypeScript function:

<Terminal cmd={['$ npx create-eas-build-function@latest ./.eas/build/myFunction']} />

This creates a new module called `myFunction` in the **.eas/build** directory. The module will contain a pre-generated module configuration and the **src** directory with the **index.ts** file containing the default TypeScript function template.

```ts .eas/build/myFunction/src/index.ts
// This file was autogenerated by `create-eas-build-function` command.
// Go to README.md to learn more about how to write your own custom build functions.

import { BuildStepContext } from '@expo/steps';

// interface FunctionInputs {
//   // specify the type of the inputs value and whether they are required here
//   // example: name: BuildStepInput<BuildStepInputValueTypeName.STRING, true>;
// }

// interface FunctionOutputs {
//   // specify the function outputs and whether they are required here
//   // example: name: BuildStepOutput<true>;
// }

async function myFunction(
  ctx: BuildStepContext
  // {
  //   inputs,
  //   outputs,
  //   env,
  // }: {
  //   inputs: FunctionInputs;
  //   outputs: FunctionOutputs;
  //   env: BuildStepEnv;
  // }
): Promise<void> {
  ctx.logger.info('Hello from my TypeScript function!');
}

export default myFunction;
```

</Step>

<Step label="2">

## Compile the function

Functions must be compiled to a single JavaScript file that can be run without installing any dependencies. The default `build` script for generated functions uses [ncc](https://github.com/vercel/ncc) to compile your function into a single file with all its dependencies. If you don't have the `ncc` installed globally on your machine, run `npm install -g @vercel/ncc` to install it. Next, run the build script in the **.eas/build/myFunction** directory:

<Terminal cmd={['$ npm run build']} />

This command triggers the `build` script placed in the **package.json** file of your custom function module.

```json package.json
{
  ...
  "scripts": {
    ...
    "build": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register" /* @end */
    ...
  },
  ...
}
```

The `build` script generates **build/index.js**. This file must be uploaded to EAS Build as a part of your project archive, so that the builder can run your function. Ensure that the file is not excluded by a **.gitignore** file or **.easignore** file.

</Step>

<Step label="3">

## Expose the function to the custom build config

> **Note**: The following example assumes that you have already set up a custom build workflow and configured it in your **eas.json**. If not, see [Get started with custom builds](/custom-builds/get-started/#create-a-workflow) before proceeding.

Let's assume that you have a **config.yml** file in the **.eas/build** directory. It contains the following content:

```yaml .eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - eas/install_node_modules
    - run:
        name: Finished
        command: echo Finished
```

To add your function to the config, you need to add the following lines to the **config.yml** file:

```yaml .eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - eas/install_node_modules
    - run:
        name: Finished
        command: echo Finished

# @info #
functions:
  my_function:
    name: My function
    path: ./myFunction
# @end #
```

The `path` property should be a relative path from the config file to your function directory. In this case, it's just `./myFunction`.

Now, add a call to the `my_function` function in the **config.yml** file:

```yaml .eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - eas/install_node_modules
    # @info #
    - my_function
    # @end #
    - run:
        name: Finished
        command: echo Finished

functions:
  my_function:
    name: My function
    path: ./myFunction
```

<ContentSpotlight
  alt="Example of a custom build config using a JavaScript/TypeScript function."
  src="/static/images/eas-build/custom-build-function.png"
/>

</Step>

<Step label="4">

## Working on the function

For a more advanced example, let's say you want to make a function calculate the sum of two numbers and print the result to the console, and then output that value from the function. To do this, modify the **config.yml** and **index.ts** files to make the function accept two inputs called `num1` and `num2` and return their sum as an output called `sum`.

```yaml .eas/build/config.yml
build:
  name: My example config
  steps:
    - eas/checkout
    - eas/install_node_modules
    # @info #
    - my_function:
        inputs:
          num1: 1
          num2: 2
        id: sum_function
    # @end #
    - run:
        name: Print the sum
        inputs:
          sum: ${ steps.sum_function.sum }
        command: echo ${ inputs.sum }
    - run:
        name: Finished
        command: echo Finished

functions:
  my_function:
    name: My function
    # @info #
    inputs:
      - name: num1
        type: number
      - name: num2
        type: number
    outputs:
      - name: sum
    # @end #
    path: ./myFunction
```

```ts .eas/build/myFunction/src/index.ts
// This file was autogenerated by `create-eas-build-function` command.
// Go to README.md to learn more about how to write your own custom build functions.

import {
  BuildStepContext,
  BuildStepInput,
  BuildStepInputValueTypeName,
  BuildStepOutput,
} from '@expo/steps';

/* @info */
interface FunctionInputs {
  // first template argument is the type of the input value, second template argument is a boolean indicating if the input is required
  num1: BuildStepInput<BuildStepInputValueTypeName.NUMBER, true>;
  num2: BuildStepInput<BuildStepInputValueTypeName.NUMBER, true>;
}
/* @end */

/* @info */
interface FunctionOutputs {
  // template argument is a boolean indicating if the output is required
  sum: BuildStepOutput<true>;
}
/* @end */

async function myFunction(
  ctx: BuildStepContext,
  {
    inputs,
    outputs,
  }: // env,
  {
    inputs: FunctionInputs;
    outputs: FunctionOutputs;
    // env: BuildStepEnv;
  }
): Promise<void> {
  /* @info */
  ctx.logger.info(`num1: ${inputs.num1.value}`);
  ctx.logger.info(`num2: ${inputs.num2.value}`);

  const sum = inputs.num1.value + inputs.num2.value;

  ctx.logger.info(`sum: ${sum}`);

  outputs.sum.set(sum.toString()); // Currently, outputs must be strings. This will improve in the future.
  /* @end */
}

export default myFunction;
```

<ContentSpotlight
  alt="Example of a custom build config using custom JavaScript/TypeScript function."
  src="/static/images/eas-build/custom-build-function-advanced.png"
/>

> **Info** Remember to compile your function each time you make changes to it: `npm run build`.

</Step>

## Summary

- Writing functions is a great way to extend the functionality of custom builds with your own logic.
- EAS Build functions are reusable — you can use them in multiple custom build configurations.
- Using EAS Build functions is a great option for more advanced use cases that are not easy to do by writing shell scripts.
- Most of the [built-in functions](/custom-builds/schema/#built-in-eas-functions) are open-source and can be forked or used as a reference for writing your own functions.

Check out the **example repository** for more detailed examples:

<BoxLink
  title="Custom build example repository"
  description="A custom EAS Build example that includes examples for workflows such as setting up functions, using environment variables, uploading artifacts, and more."
  Icon={GithubIcon}
  href="https://github.com/expo/eas-custom-builds-example/tree/main"
/>
